package com.berwin.caper.sftp;

import com.jcraft.jsch.*;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class CaperSFTPTransfer {
    private static final String NO_SUCH_FILE = "No such file";
    public static String LOCAL_REFERENCE_HOST = "127.0.0.1";
    public static int LOCAL_REFERENCE_PORT = 12358;

    private String jumpHost;
    private int jumpPort;
    private String jumpName;
    private String jumpPassword;

    private String remoteName;  // 远程服务器名字
    private String remoteHost;
    private int remotePort;

    private Session jumpSession;
    private Session remoteSession;
    private ChannelSftp remoteSftpChannel;

    public CaperSFTPTransfer(String jumpHost, int jumpPort, String jumpName, String jumpPassword, String remoteHost, int remotePort) {
        this.jumpHost = jumpHost;
        this.jumpPort = jumpPort;
        this.jumpName = jumpName;
        this.jumpPassword = jumpPassword;
        this.remoteHost = remoteHost;
        this.remotePort = remotePort;
    }

    public boolean connect() {
        try {
            JSch jSch = new JSch();
            jumpSession = jSch.getSession(jumpName, jumpHost, jumpPort);
            jumpSession.setPassword(jumpPassword);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshConfig.put("PreferredAuthentications", "password,keyboard-interactive");
            jumpSession.setConfig(sshConfig);
            jumpSession.connect(1000 * 30);
            remoteSftpChannel = (ChannelSftp) jumpSession.openChannel("sftp");
            remoteSftpChannel.connect(1000 * 30);
            this.log("连接成功");
            return true;
        } catch (Exception e) {
            this.log("连接失败");
            e.printStackTrace();
        } finally {
//            if (jumpSession != null && jumpSession.isConnected())
//                jumpSession.disconnect();
//            if (remoteSession != null && remoteSession.isConnected())
//                remoteSession.disconnect();
//            if (remoteSftpChannel != null && remoteSession.isConnected())
//                remoteSftpChannel.disconnect();

        }
        return false;
    }

    public void disconnect() {
        if (this.isConnected()) {
            this.remoteSftpChannel.disconnect();
        }
    }

    /**
     * 跳转到某台服务器
     *
     * @param target /sftp_root@Test-BUYU(vpc-2ze8rcbunydqzw7vatezk)_172.17.16.29:12358
     */
    public void jump(String target) {
        if (!this.isConnected())
            return;
        try {
            this.remoteName = target;
            remoteSftpChannel.cd(target);
        } catch (SftpException e) {
            this.log("跳板机跳转失败:" + target);
            e.printStackTrace();
        }
    }

    /**
     * 进入目录
     *
     * @param remoteDir 进入的目录
     */
    public void cd(String remoteDir) {
        if (!this.isConnected())
            return;
        try {
            this.remoteSftpChannel.cd(remoteDir);
        } catch (SftpException e) {
            e.printStackTrace();
        }
    }


    /**
     * 上传文件
     *
     * @param remoteDir 服务器路径
     * @param filename  文件名
     * @param localFile 本地文件路径
     */
    public boolean upload(String remoteDir, String filename, String localFile) {
        if (!this.isConnected()) {
            return false;
        }
        this.log("[上传]:" + localFile + " -> " + remoteDir + File.separator + filename);
        try {
            this.isRemoteDirectoryExist(remoteDir);
            this.remoteSftpChannel.cd(remoteDir);
            InputStream is = new FileInputStream(localFile);
            this.remoteSftpChannel.put(is, filename);
            this.log("[上传]上传成功!");
            return true;
        } catch (SftpException | FileNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 上传文件夹
     *
     * @return
     */
    public boolean uploadFolder(String remoteDir, String localDir) {
        List<File> files = new ArrayList<>();
        this.visitAllDirsAndFiles(new File(localDir), files);
        for (File file : files) {
            try {
                String fileName = file.getName();
                String filePath = file.getCanonicalPath();
                String absPath = filePath.replace(localDir, "");
                absPath = absPath.replace(fileName, "");
                if (!this.upload(remoteDir + absPath, fileName, filePath)) {
                    this.log("文件夹中文件上传失败:" + filePath);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 下载远程文件
     *
     * @param remoteDir 远程路径
     * @param filename  文件名
     * @param localDir  本地路径
     * @return
     */
    public boolean download(String remoteDir, String filename, String localDir) {
        if (!this.isConnected())
            return false;
        try {
            this.remoteSftpChannel.cd(remoteDir);
            this.log("[下载]:" + remoteDir + File.separator + filename + " -> " + localDir + File.separator + filename);
            InputStream is = this.remoteSftpChannel.get(filename);
            byte[] bytes = new byte[1024];
            FileOutputStream fos = new FileOutputStream(localDir + File.separator + filename);
            int idx;
            while ((idx = is.read(bytes)) != -1) {
                fos.write(bytes, 0, idx);
                fos.flush();
            }
            fos.close();
            is.close();
            return true;
        } catch (SftpException | FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 目录下载
     *
     * @param remoteDir 远程路径
     * @param localDir  本地路径
     * @return
     */
    public boolean downloadFolder(String remoteDir, String localDir) {
        // 这里需要遍历服务器下面的目录
//        List<File> files = new ArrayList<>();
//        this.visitAllDirsAndFiles(new File(localDir), files);
//        for (File file : files) {
//            try {
//                String filePath = file.getCanonicalPath();
//                String absPath = filePath.replace(localDir, "");
//                String fileName = file.getName();
//                this.download(remoteDir, fileName, localDir + absPath);
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }
        return false;
    }

    private void visitAllDirsAndFiles(File dir, List<File> files) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                visitAllDirsAndFiles(new File(dir, children[i]), files);
            }
        } else {
            files.add(dir);
        }
    }

    /**
     * 服务器目录是否存在
     *
     * @return
     */
    private void isRemoteDirectoryExist(String path) throws SftpException {
        try {
            SftpATTRS attrs = this.remoteSftpChannel.lstat(path);
            this.log(path + " 是否目录:" + attrs.isDir());
        } catch (SftpException e) {
            if (NO_SUCH_FILE.equals(e.getMessage())) {
                this.remoteSftpChannel.cd("/"); // 先重置到根目录
                this.log(this.remoteHost + this.remoteName + " 不存在，创建该路径:" + path);
                String[] paths = path.split(File.separator);
                String _tmp = "";
                for (String p : paths) {
                    if ("".equals(p))
                        continue;
                    _tmp += File.separator + p;
                    try {
                        if (!this.remoteSftpChannel.lstat(_tmp).isDir())
                            this.remoteSftpChannel.mkdir(p);
                        this.remoteSftpChannel.cd(p);
                    } catch (SftpException e1) {
                        this.remoteSftpChannel.mkdir(p);
                        this.remoteSftpChannel.cd(p);
                    }
                }
            } else {
                throw e;
            }
        }
    }

    public boolean isConnected() {
        if (this.jumpSession == null || !this.jumpSession.isConnected() || this.remoteSftpChannel == null || !this.remoteSftpChannel.isConnected()) {
            this.log("远程SFTP通道为空");
            return false;
        }
        return true;
    }

    public String getJumpHost() {
        return jumpHost;
    }

    public void setJumpHost(String jumpHost) {
        this.jumpHost = jumpHost;
    }

    public int getJumpPort() {
        return jumpPort;
    }

    public void setJumpPort(int jumpPort) {
        this.jumpPort = jumpPort;
    }

    public String getJumpName() {
        return jumpName;
    }

    public void setJumpName(String jumpName) {
        this.jumpName = jumpName;
    }

    public String getJumpPassword() {
        return jumpPassword;
    }

    public void setJumpPassword(String jumpPassword) {
        this.jumpPassword = jumpPassword;
    }

    public String getRemoteHost() {
        return remoteHost;
    }

    public void setRemoteHost(String remoteHost) {
        this.remoteHost = remoteHost;
    }

    public int getRemotePort() {
        return remotePort;
    }

    public void setRemotePort(int remotePort) {
        this.remotePort = remotePort;
    }

    public ChannelSftp getRemoteSftpChannel() {
        return remoteSftpChannel;
    }

    public void setRemoteSftpChannel(ChannelSftp remoteSftpChannel) {
        this.remoteSftpChannel = remoteSftpChannel;
    }

    private void log(String txt) {
        System.out.println("[CaperSFTP]" + txt);
    }
    
//    private boolean is
}
